#include <stdio.h>
#include <string.h>
#include "fat32.h"
#include "direccionamiento.h"
#include "utils.h"
#include "directorio.h"

#define SIZE_SECTOR 512
#define DEBUG

extern unsigned int SECTOR_PER_CLUSTER;
extern unsigned int *entrada_FAT;
extern FILE *fp;
extern struct boot_sector_values* v_boot_sector;
extern struct FS_information_sector_values * v_FS_information_sector;

unsigned int FAT_armar_nombre_corto(const char * elemento, char * nombre);
unsigned int FAT_construir_numero_cluster_fat32(const ENTRADA83 * entrada);
SUPER_CACHO FAT_traer_contenido_de_directorio(char *);

unsigned int * FAT_llenar_Tabla_FAT(struct boot_sector_values v_boot_sector,
		FILE *fp) {
	unsigned long posicion_arranque_tabla_FAT;
	unsigned long posicion_final_tabla_FAT;
	unsigned int *entrada_FAT;
	posicion_arranque_tabla_FAT =
			(unsigned long) (v_boot_sector.value_reserved_sector_count
					* SIZE_SECTOR);

	posicion_final_tabla_FAT = posicion_arranque_tabla_FAT
			+ (v_boot_sector.value_sectors_per_fat_table * SIZE_SECTOR);

	//primero creo un vector de la cantidad de entradas necesarias
	entrada_FAT = malloc(FAT_largo_tabla_FAT_bytes(v_boot_sector));

	//lleno el vector con las entradas fat U.U
	unsigned char *desplazador_FAT;
	desplazador_FAT = entrada_FAT;

#ifdef DEBUG
	printf("\nPosición sector de arranque FAT: %d",
			posicion_arranque_tabla_FAT);
	printf("\nSector de arranque FAT: %d",
			v_boot_sector.value_reserved_sector_count);
	printf(
			"\nFinal de tabla FAT: %d",
			v_boot_sector.value_sectors_per_fat_table
					+ v_boot_sector.value_reserved_sector_count);
	printf("\nPosición sector de final FAT: %d", posicion_final_tabla_FAT);
	printf("\nLargo del malloc para la tabla FAT32: %d",
			(v_boot_sector.value_sectors_per_fat_table * SIZE_SECTOR));
#endif

	char * bloque;
	bloque = malloc(SIZE_SECTOR * sizeof(char));
	unsigned int i;

	for (i = v_boot_sector.value_reserved_sector_count;
			i
					< v_boot_sector.value_reserved_sector_count
							+ v_boot_sector.value_sectors_per_fat_table; i++) {
		bloque = leer_sector(i, fp);
		dcc_llenar_desde_cluster(desplazador_FAT, bloque, 0, SIZE_SECTOR);
		desplazador_FAT = desplazador_FAT + (SIZE_SECTOR);
		free(bloque);
	}
	bloque = NULL;
	printf("\nFAT Leída :)");
	return entrada_FAT;
}

unsigned long FAT_clusters_libres_en_FAT(int* tabla_FAT,
		unsigned long largo_tabla_FAT) {
	unsigned long acum;
	acum = 0;
	int i;
	for (i = 0; i < largo_tabla_FAT; i++) {
		if (tabla_FAT[i] == CLUSTER_LIBRE
			)
			acum = acum + 1;
	}
	return acum;

}
unsigned long FAT_largo_tabla_FAT_bytes(struct boot_sector_values v_boot_sector) {
	return (v_boot_sector.value_sectors_per_fat_table * SIZE_SECTOR);
}
void FAT_byte_a_sector(unsigned long byteIngreso, unsigned long *sector,
		unsigned long *cluster, unsigned long *byte) {
	*cluster = (unsigned long) (byteIngreso / (unsigned long) SIZE_SECTOR
			/ (unsigned long) SECTOR_PER_CLUSTER);
	*sector = (unsigned long) (byteIngreso / SIZE_SECTOR);
	*byte = byteIngreso - (*cluster * SIZE_SECTOR * SECTOR_PER_CLUSTER);
	*sector = (*byte / 512);
	*byte = byteIngreso - (*cluster * SIZE_SECTOR * SECTOR_PER_CLUSTER)
			- (*sector * SIZE_SECTOR);
}

//Recordar que la responsabilidad de hacer free pertenece a quién llama a esta función
//devuelve el largo en bytes del bloque de memoria leído.
char* FAT_traer_informacion_encadenada(unsigned long cluster_inicio,
		unsigned int * count) {
	printf("\nProceso el cluster %d", cluster_inicio);
	//unsigned int count;
	unsigned int i;
	unsigned int SIZE_CLUSTER;
	char * puntero;

	SIZE_CLUSTER = SIZE_SECTOR * SECTOR_PER_CLUSTER;

	*count = 1;
	puntero = dcc_leer_un_cluster(cluster_inicio, fp);

	i = cluster_inicio;

	while (abs(entrada_FAT[i] - CLUSTER_OCUPADO) > 0X7) {
		//printf ("\nEntro al while!!");
		switch (entrada_FAT[i]) {
		case CLUSTER_LIBRE: {
			printf("Hay un cluster libre\n");
			break;
		}
		case CLUSTER_RESERVADO:
		case CLUSTER_RESERVADO2: {
			printf("Hay un cluster reservado\n");
			break;
		}
		case CLUSTER_CORRUPTO: {
			printf("Hay un cluster corrupto!!!\n");
			break;
		}
		}
		//se supone que es un cluster válido
		printf("\nCount en %d", *count);
		(*count)++;
		char* sarasa = malloc((*count) * SIZE_CLUSTER);
		memcpy(sarasa, puntero, ((*count) - 1) * SIZE_CLUSTER);
		free(puntero);
		puntero = NULL;
		i = entrada_FAT[i];

		char * cluster = dcc_leer_un_cluster(i, fp);
		memcpy(sarasa + ((*count) - 1) * SIZE_CLUSTER, cluster, SIZE_CLUSTER);
		free(cluster);
		puntero = sarasa;
		//puntero+((count-1) * SIZE_CLUSTER) = dcc_leer_un_cluster(i,fp);
	}

	//printf("\n Devuelvo lo que está");
	return puntero;
	//return count * SIZE_CLUSTER;
}

unsigned int FAT_obtener_cluster_inicial_para(char * cacho_de_memoria,
		unsigned int largo_cacho_de_memoria, char * cadena_directorio) {
	ENTRADA83 *entrada;
	//Listamos las entradas una por una
	entrada = malloc(sizeof(ENTRADA83));

	memcpy(entrada, cacho_de_memoria, sizeof(ENTRADA83));
	int count;
	count = 1;
	while (entrada->nombre[0] != 0x00) //&& largo_cacho_de_memoria >= count*sizeof(ENTRADA83))
	{

		if (entrada->atributos != ATR_NOM_LARGO)
		{
			char nombre[12 + 1];FAT_armar_nombre_corto(entrada->nombre, nombre);
			//printf("Nombre Corto: %s\n",nombre);
			//filler(buf, nombre , NULL, 0);
			if (!strcmp(cadena_directorio,nombre))
			{
				unsigned int valor_a_devolver;
				valor_a_devolver = FAT_construir_numero_cluster_fat32(entrada);
				free (entrada);
				entrada = NULL;
				return valor_a_devolver;
			}
		}

		else
		{
			char nombre[13 + 1];

			FAT_armar_parte_de_nombre((ENTRADA_LARGA*) entrada, nombre);
			//printf("Nombre Largo: %s\n",nombre);
			//filler(buf, nombre, NULL, 0);
			if (!strcmp(cadena_directorio,nombre))
			{
				//free(entrada);
				//entrada = malloc(sizeof(ENTRADA83));
				memcpy(entrada,cacho_de_memoria + count*sizeof(ENTRADA83),sizeof(ENTRADA83));
				unsigned int valor_a_devolver;
				valor_a_devolver = FAT_construir_numero_cluster_fat32(entrada);
				free (entrada);
				entrada = NULL;
				return valor_a_devolver;
			}
			count++;
		}

		//Vuelvo a leer otra entrada
		//free(entrada);
		//entrada = malloc(sizeof(ENTRADA83));
		memcpy(entrada,cacho_de_memoria+count*sizeof(ENTRADA83),sizeof(ENTRADA83));
		count++;
	}
	free(entrada);
	entrada = NULL;
	printf("\nLA ENTRADA BUSCADA ES INVALIDA '%s'\n", cadena_directorio);
	return 0;

}

unsigned int FAT_construir_numero_cluster_fat32(const ENTRADA83 * entrada) {
	unsigned short valor_devuelto[2];
	unsigned int valor;
	valor_devuelto[0] = entrada->direccion_baja;
	printf("\nEl valor de baja es: %d", entrada->direccion_baja);
	valor_devuelto[1] = entrada->direccion_alta;
	printf("\nEl valor de alta es: %d", entrada->direccion_alta);
	memcpy(&valor, valor_devuelto, sizeof(unsigned int));
	return valor;
}

SUPER_CACHO FAT_traer_contenido_de_directorio(char * path) {
	unsigned int * largo_entrada;
	largo_entrada = malloc(sizeof(unsigned int));
	unsigned int nuevo_cluster;
	char * cacho_de_memoria;
	char * tabla;
	SUPER_CACHO bloque;
	nuevo_cluster = 2;
	cacho_de_memoria = FAT_traer_informacion_encadenada(nuevo_cluster,
			largo_entrada);

	//printf("\nspliteo el string '%s'\n", path);
	if (strcmp(path, "/")) {
		tabla = strtok(path, "/");
		//tabla = (char *)FAT_split_string(path,'/');
		unsigned int contador;
		contador = 0;
		while (tabla != NULL) {
			printf("\Traigo el dato para '%s'", tabla);
			nuevo_cluster = FAT_obtener_cluster_inicial_para(cacho_de_memoria,
					*largo_entrada, tabla);
			free(cacho_de_memoria);
			if (!nuevo_cluster) {
				//ERROR! No se encuentra la entrada en el cacho de memeoria
				bloque.cacho_de_memoria = NULL;
				return bloque;
			}
			cacho_de_memoria = FAT_traer_informacion_encadenada(nuevo_cluster,
					largo_entrada);
			//contador+=14;
			tabla = strtok(NULL, "/");
		}
	}
	bloque.cacho_de_memoria = cacho_de_memoria;
	bloque.cluster_inicial = nuevo_cluster;
	bloque.cantidad_de_clusters = largo_entrada;
	free(largo_entrada);
	largo_entrada = NULL;
	return bloque;
}

unsigned int FAT_armar_nombre_corto(const char * elemento, char * nombre) {

	char extension[3 + 1];

nombre	[8] = extension[3] = '\0';
	strncpy(extension, elemento + 8, 3);
	strncpy(nombre, elemento, 8);
	strcat(nombre, ".");
	strcat(nombre, extension);

	return 0;
}

int FAT_armar_parte_de_nombre(const ENTRADA_LARGA * entrada, char * destino) {

	int numero_magico;
	int count;
	char todos[26];
	memcpy(todos, entrada->cinco_caracteres, 10);
	memcpy(todos + 10, entrada->seis_caracteres, 12);
	memcpy(todos + 22, entrada->dos_caracteres, 4);
	numero_magico = 13;

	count = 0;
	while (todos[count] != 0) {
		unicode_utf16_to_utf8_inbuffer(
				(uint16_t *) (todos + count), 1 , destino + count / 2, &numero_magico)
				;
				//printf("todos: %s, count:%d, todos + count: %s", todos + count, count, todos + count + 2);
				count+=
		2;
	}

	//printf("%s\n",destino);

	/*if (1 != unicode_utf16_to_utf8_inbuffer((uint16_t *)entrada->cinco_caracteres, cinco , los5, &cinco))
	 {
	 perror("NO SE PUEDE CONSTRUIR EL NOMBRE LARGO A PARTIR DE LA ENTRADA LARGA CON EL PRIMER ELEMENTO");
	 return 1;
	 }
	 printf("Los 5: %s\n",los5);

	 if (1 != unicode_utf16_to_utf8_inbuffer((uint16_t *)entrada->seis_caracteres, 3, los6, &seis))
	 {
	 perror("NO SE PUEDE CONSTRUIR EL NOMBRE LARGO A PARTIR DE LA ENTRADA LARGA CON EL SEGUNDO ELEMENTO");
	 return 1;
	 }
	 printf("Los 6: %s\n",los6);

	 if (1 != unicode_utf16_to_utf8_inbuffer((uint16_t *)entrada->dos_caracteres, 0, los2, &dos))
	 {
	 perror("NO SE PUEDE CONSTRUIR EL NOMBRE LARGO A PARTIR DE LA ENTRADA LARGA CON EL TERCER ELEMENTO");
	 return 1;
	 }
	 printf("Los 2: %s\n",los2);

	 *destino = '\0';
	 strcat(destino, los5);
	 strcat(destino, los6);
	 strcat(destino, los2);
	 */
	return 0;

}
char * obtener_archivo_de_path(char *path) {
	int i = 0;
	char *puntero;
	char *destino;
	destino = malloc(strlen(path) + 1);
	while (path[i] != '\0') {
		if (path[i] == '/') {
			puntero = path + i;
		}
		i++;
	}
	strcpy(destino, puntero + 1);
	return destino;
}
char * obtener_path_sin_archivo(char * path) {
	char s2 = '/';
	char *valor_devolucion;
	valor_devolucion = malloc(strlen(path) + 1);
	strcpy(valor_devolucion, path);
	int i;
	int posicion;
	posicion = 0;
	i = 0;
	while (path[i] != '\0') {
		if (path[i] == s2)
			posicion = i;
		i++;
	}
	if (posicion == 0) {
		strcpy(valor_devolucion, "/");
	} else {
		valor_devolucion[posicion] = '\0';
	}
	return valor_devolucion;
}

ENTRADA83 * obtener_entrada_para(char * path) {
//unsigned int *largo_entrada;
//largo_entrada = malloc(sizeof(unsigned int));

	SUPER_CACHO bloque;
	char *path_sin_archivo;
	char *archivo;
	ENTRADA83 *entrada;
	char nombre[13 + 1];

path_sin_archivo	= obtener_path_sin_archivo(path);
//printf("Vamos por el path %s \n",path_sin_archivo);

	archivo = obtener_archivo_de_path(path);
	printf("Vamos por el path %s y el archivo %s\n", path_sin_archivo, archivo);

	bloque = FAT_traer_contenido_de_directorio(path_sin_archivo);
	free(path_sin_archivo);
	path_sin_archivo = NULL;

	entrada = malloc(sizeof(ENTRADA83));
	memset(entrada, 0, sizeof(ENTRADA83));

	memcpy(entrada, bloque.cacho_de_memoria, sizeof(ENTRADA83));
	int count;
	count = 1;

	while (entrada->nombre[0] != 0x00) {

		if (entrada->atributos != ATR_NOM_LARGO)
		{
			FAT_armar_nombre_corto(entrada->nombre, nombre);

		}

		else {
			FAT_armar_parte_de_nombre((ENTRADA_LARGA*) entrada, nombre);

			free(entrada);
			entrada = NULL;
			entrada = malloc(sizeof(ENTRADA83));
			memset(entrada, 0, sizeof(ENTRADA83));
			memcpy(entrada, bloque.cacho_de_memoria + count * sizeof(ENTRADA83),
					sizeof(ENTRADA83));
			count++;
		}
		//Verifico el nombre
		if (!strcmp(archivo, nombre)) {
			printf("ENCUENTRO UNA BIEN %s\n", archivo);
			free(archivo);
			archivo = NULL;
			free(bloque.cacho_de_memoria);
			bloque.cacho_de_memoria = NULL;
			return entrada;
		}
		//else
		//printf("\nNo matchea entrada\n");
		//Vuelvo a leer otra entrada
		free(entrada);
		entrada = NULL;
		entrada = malloc(sizeof(ENTRADA83));
		memcpy(entrada, bloque.cacho_de_memoria + count * sizeof(ENTRADA83),
				sizeof(ENTRADA83));
		count++;
	}

	free(entrada);
	entrada = NULL;
	printf("\nNo se encuentra la entrada de directorio con el nombre %s\n",
			archivo);

	free(bloque.cacho_de_memoria);
	bloque.cacho_de_memoria = NULL;

	free(archivo);
	archivo = NULL;
//free(largo_entrada);
//largo_entrada=NULL;
	return NULL;
}

/*----------------------------------
 * FUNCIONES USADAS PARA ESCRITURA
 * -------------------------------*/

unsigned int FAT_agregar_nueva_entrada(char *path, char tipo) {
	char *sin_archivo, *archivo;
	SUPER_CACHO bloque;
	unsigned int i, bandera, cantidad_de_caracteres, entradas_necesarias;
	ENTRADA83 entrada, entrada_aux, entrada_larga_aux;
	sin_archivo = obtener_path_sin_archivo(path);
	archivo = obtener_archivo_de_path(path);
	bloque = FAT_traer_contenido_de_directorio(sin_archivo);
	cantidad_de_caracteres = sizeof(archivo);

	entradas_necesarias = 1;
	if (cantidad_de_caracteres > 11) {
		entradas_necesarias = 2;
	}

	i = 0;
	bandera = 1;
	//memcpy(&entrada, bloque.cacho_de_memoria, sizeof(ENTRADA83));

	do {
		memcpy(&entrada, bloque.cacho_de_memoria + (i * sizeof(ENTRADA83)),
				sizeof(ENTRADA83));
		if (entrada.nombre[0] != '\0')
			bandera = 0;
		else if (entrada.nombre[0] == 0xE5) {
			if (entradas_necesarias == 2) {
				memcpy(&entrada,
						bloque.cacho_de_memoria + ((i + 1) * sizeof(ENTRADA83)),
						sizeof(ENTRADA83));
				if (entrada.nombre[0] == 0xE5)
					bandera = 0;
				;
			}
			if (entradas_necesarias == 1)
				bandera = 0;
			;
		}
		i++;
	} while (bandera);
	i--;

	if (entradas_necesarias == 1) {
		grabar_nombre_corto(archivo, &entrada_aux);
	} else {
		grabar_nombre_largo(archivo, &entrada_larga_aux);
	}
	grabar_atributos(&entrada_aux, tipo);

	if (entrada.nombre[0] == 0xE5) {
		if (entradas_necesarias == 1) {
			//memcpy(bloque.cacho_de_memoria + (i * sizeof(ENTRADA83)), entrada_aux, sizeof(ENTRADA83));
		} else {
			//memcpy(bloque.cacho_de_memoria + (i * sizeof(ENTRADA83)), entrada_larga_aux, sizeof(ENTRADA83));
			//memcpy(bloque.cacho_de_memoria + ((i + 1) * sizeof(ENTRADA83)), entrada_aux, sizeof(ENTRADA83));
		}
	} else {

	}

}

void grabar_nombre_corto(char *nombre, ENTRADA83 *entrada) {
	int i, j;

	i = 0;
	j = 0;
	while (nombre[i] != '\0') {
		if (nombre[i] != '.') {
			entrada->nombre[j] = nombre[i];
			j++;
		}
		i++;
	}
	//return j;
}

void grabar_nombre_largo(char *nombre, ENTRADA_LARGA *entrada) {
	int i, numero_magico;
	uint16_t caracter16;
	numero_magico = 1;
	for (i = 0; i < 13; i++) {
		unicode_utf8_to_utf16_inbuffer(nombre + i, 1, &caracter16,
				&numero_magico);
		if (i < 5) {
			memcpy(entrada->cinco_caracteres + (i * 2), caracter16,
					sizeof(uint16_t));
		} else if (i < 11) {
			memcpy(entrada->seis_caracteres + ((i % 6) * 2), caracter16,
					sizeof(uint16_t));
		} else {
			memcpy(entrada->dos_caracteres + ((i % 11) * 2), caracter16,
					sizeof(uint16_t));
		}

	}
}

void grabar_atributos(ENTRADA83 *entrada, char *tipo) {
	unsigned short direccion[2];
	(*entrada).atributos = *tipo;
	(*entrada).tamanio = 0;
	//direccion = FAT_primer_cluster_libre_en_FAT(entrada_FAT,(FAT_largo_tabla_FAT_bytes(v_boot_sector)/4))
	(*entrada).direccion_alta = direccion[1];
	(*entrada).direccion_baja = direccion[0];
}
int FAT_primer_cluster_libre_en_FAT(int* tabla_FAT,
		unsigned long largo_tabla_FAT) {
	int i;
	i = 0;
	while (i < largo_tabla_FAT && tabla_FAT[i] != CLUSTER_LIBRE) {
		i++;
	}
	if (i == largo_tabla_FAT) {
		//FAT LLENA
		printf("\nNo le da mas el coco a la fat\n");
		return 0;
	}
	return i;

}
